package rexsee.cartoon.component;

import java.util.ArrayList;

import rexsee.cartoon.Action;
import rexsee.cartoon.Cartoons;
import rexsee.cartoon.Frame;
import rexsee.cartoon.Parser;
import rexsee.cartoon.TagName;
import rexsee.core.browser.clazz.LogListeners;
import rexsee.core.utilities.Utilities;
import rexsee.xml.XmlElement;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.Rect;

public class Scrolling extends Cartoon {

	public static final int DIRECTION_UP = 0;
	public static final int DIRECTION_DOWN = 1;
	public static final int DIRECTION_LEFT = 2;
	public static final int DIRECTION_RIGHT = 3;
	public static final int DIRECTION_FORWARD = 4;
	public static final int DIRECTION_BACKWARD = 5;

	private int direction = DIRECTION_RIGHT;
	private int centerX = 0;
	private int centerY = 0;
	public float step = 30;
	public float threshold = 0.05f;

	private float currentFrameOffset = 0;
	private int currentFrameIndex = 0;
	private float[] zOffset;
	private final boolean isSupportClipPath = Cartoon.isSupportClipPath();

	public Scrolling(Parser parser, XmlElement element) {

		super(parser, element);
		if (element == null || element.attributes == null) return;
		step = Utilities.getFloat(element.attributes.get(TagName.ATTR_CARTOON_STEP), step);
		threshold = Utilities.getFloat(element.attributes.get(TagName.ATTR_CARTOON_THRESHOLD), threshold);

		String center = element.attributes.get(TagName.ATTR_CARTOON_CENTER);
		int[] ints = Utilities.getIntArray(center, 2, 0);
		if (ints != null) setCenter(ints[0], ints[1]);

		String direction = element.attributes.get(TagName.ATTR_CARTOON_DIRECTION);
		setDirection(direction);

	}

	public void setCenter(int x, int y) {
		centerX = x;
		centerY = y;
	}
	public void setDirection(String direction) {
		if (direction == null) {
		} else if (direction.equalsIgnoreCase("forward")) {
			this.direction = DIRECTION_FORWARD;
			zOffset = new float[]{1, threshold};
		} else if (direction.equalsIgnoreCase("backward")) {
			this.direction = DIRECTION_BACKWARD;
			zOffset = new float[]{1, 1 / threshold};
		} else if (direction.equalsIgnoreCase("up")) {
			this.direction = DIRECTION_UP;
		} else if (direction.equalsIgnoreCase("down")) {
			this.direction = DIRECTION_DOWN;
		} else if (direction.equalsIgnoreCase("right")) {
			this.direction = DIRECTION_RIGHT;
		} else {
			this.direction = DIRECTION_LEFT;
		}
	}

	private void goForward(Canvas canvas) {

		Action act = getCurrentAction();
		if (act == null) return;

		zOffset[0] *= 1 + step;
		zOffset[1] *= 1 + step;
		if (zOffset[1] >= 1) {
			zOffset[0] = 1;
			zOffset[1] = threshold;
			currentFrameIndex++;
			if (currentFrameIndex > act.list.size() - 1) currentFrameIndex = 0;
		}

		Frame frame = act.list.get(currentFrameIndex);
		if (frame == null) return;
		if (frame.reportScroll != null) {
			for (int i = 0; i < frame.reportScroll.length; i++) {
				float r = frame.reportScroll[i];
				if (Math.abs(r - zOffset[0]) <= 0.01) {
					parser.browser.eventList.run(Cartoons.EVENT_ONSCROLLEDAT, new String[]{getId(), act.id, String.valueOf(currentFrameIndex), String.valueOf(zOffset[0]), String.valueOf(r)});
				}
			}
		}

		int nextFrameIndex = currentFrameIndex + 1;
		if (nextFrameIndex > act.list.size() - 1) nextFrameIndex = 0;
		Frame nextFrame = act.list.get(nextFrameIndex);
		if (nextFrame == null) return;
		if (nextFrame.reportScroll != null) {
			for (int i = 0; i < nextFrame.reportScroll.length; i++) {
				float r = nextFrame.reportScroll[i];
				if (Math.abs(r - zOffset[1]) <= 0.01) {
					parser.browser.eventList.run(Cartoons.EVENT_ONSCROLLEDAT, new String[]{getId(), act.id, String.valueOf(nextFrameIndex), String.valueOf(zOffset[1]), String.valueOf(r)});
				}
			}
		}

		Path path = new Path();
		path.addRect(position.rect.left, position.rect.top, position.rect.right, position.rect.bottom, Path.Direction.CW);

		try {

			Matrix matrix = new Matrix();
			matrix.postScale(zOffset[0], zOffset[0], centerX, centerY);

			canvas.save();
			if (isSupportClipPath) canvas.clipPath(path);
			canvas.concat(matrix);
			frame.load();
			canvas.drawBitmap(frame.bitmap,
					new Rect(0, 0, frame.frameWidth, frame.frameHeight),
					new Rect(position.rect.left, position.rect.top, position.rect.right, position.rect.bottom),
					null);
			canvas.restore();

			matrix.reset();
			matrix.postScale(zOffset[1], zOffset[1], centerX, centerY);

			canvas.save();
			if (isSupportClipPath) canvas.clipPath(path);
			canvas.concat(matrix);
			nextFrame.load();
			canvas.drawBitmap(nextFrame.bitmap,
					new Rect(0, 0, nextFrame.frameWidth, nextFrame.frameHeight),
					new Rect(position.rect.left, position.rect.top, position.rect.right, position.rect.bottom),
					null);
			canvas.restore();

		} catch (Exception e) {
			String error = "Scrolling Cartoon running error:" + e.getLocalizedMessage();
			parser.logListener.run(parser.context, parser.browser, LogListeners.LOG_TYPE_EXCEPTION, error);
		}

		ArrayList<Frame> usedFrame = new ArrayList<Frame>();
		usedFrame.add(frame);
		if (!usedFrame.contains(nextFrame)) usedFrame.add(nextFrame);
		act.destroyPartially(usedFrame);

	}
	private void goBackward(Canvas canvas) {

		Action act = getCurrentAction();
		if (act == null) return;

		zOffset[0] /= 1 + step;
		zOffset[1] /= 1 + step;
		if (zOffset[1] <= 1) {
			zOffset[0] = 1;
			zOffset[1] = 1 / threshold;
			currentFrameIndex--;
			if (currentFrameIndex < 0) currentFrameIndex = act.list.size() - 1;
		}

		Frame frame = act.list.get(currentFrameIndex);
		if (frame == null) return;
		if (frame.reportScroll != null) {
			for (int i = 0; i < frame.reportScroll.length; i++) {
				float r = frame.reportScroll[i];
				if (Math.abs(r - zOffset[0]) <= 0.01) {
					parser.browser.eventList.run(Cartoons.EVENT_ONSCROLLEDAT, new String[]{getId(), act.id, String.valueOf(currentFrameIndex), String.valueOf(zOffset[0]), String.valueOf(r)});
				}
			}
		}

		int nextFrameIndex = currentFrameIndex - 1;
		if (nextFrameIndex < 0) nextFrameIndex = act.list.size() - 1;
		Frame nextFrame = act.list.get(nextFrameIndex);
		if (nextFrame == null) return;
		if (nextFrame.reportScroll != null) {
			for (int i = 0; i < nextFrame.reportScroll.length; i++) {
				float r = nextFrame.reportScroll[i];
				if (Math.abs(r - zOffset[1]) <= 0.01) {
					parser.browser.eventList.run(Cartoons.EVENT_ONSCROLLEDAT, new String[]{getId(), act.id, String.valueOf(nextFrameIndex), String.valueOf(zOffset[1]), String.valueOf(r)});
				}
			}
		}

		Path path = new Path();
		path.addRect(position.rect.left, position.rect.top, position.rect.right, position.rect.bottom, Path.Direction.CW);

		try {

			Matrix matrix = new Matrix();
			matrix.postScale(zOffset[1], zOffset[1], centerX, centerY);

			canvas.save();
			if (isSupportClipPath) canvas.clipPath(path);
			canvas.concat(matrix);
			nextFrame.load();
			canvas.drawBitmap(nextFrame.bitmap,
					new Rect(0, 0, nextFrame.frameWidth, nextFrame.frameHeight),
					new Rect(position.rect.left, position.rect.top, position.rect.right, position.rect.bottom),
					null);
			canvas.restore();

			matrix.reset();
			matrix.postScale(zOffset[0], zOffset[0], centerX, centerY);

			canvas.save();
			if (isSupportClipPath) canvas.clipPath(path);
			canvas.concat(matrix);
			frame.load();
			canvas.drawBitmap(frame.bitmap,
					new Rect(0, 0, frame.frameWidth, frame.frameHeight),
					new Rect(position.rect.left, position.rect.top, position.rect.right, position.rect.bottom),
					null);
			canvas.restore();

		} catch (Exception e) {
			String error = "Scrolling Cartoon running error:" + e.getLocalizedMessage();
			parser.logListener.run(parser.context, parser.browser, LogListeners.LOG_TYPE_EXCEPTION, error);
		}

		ArrayList<Frame> usedFrame = new ArrayList<Frame>();
		usedFrame.add(frame);
		if (!usedFrame.contains(nextFrame)) usedFrame.add(nextFrame);
		act.destroyPartially(usedFrame);

	}
	private void goLeft(Canvas canvas) {

		Action act = getCurrentAction();
		if (act == null) return;

		int rectWidth = position.rect.width();

		int nextFrameOffset = (int) (currentFrameOffset + step);
		boolean goNext = false;
		boolean isFirst = true;

		int rest = rectWidth;
		int index = currentFrameIndex;

		ArrayList<Frame> usedFrame = new ArrayList<Frame>();
		while (rest > 0) {
			Frame frame = act.list.get(index);
			if (frame != null) {
				try {
					int srcLeft;
					if (isFirst) {
						srcLeft = (int) currentFrameOffset;
						isFirst = false;
						if (frame.reportScroll != null) {
							for (int i = 0; i < frame.reportScroll.length; i++) {
								int r = (int) frame.reportScroll[i];
								if (Math.abs(r - srcLeft) <= Math.floor(step / 2f)) {
									parser.browser.eventList.run(Cartoons.EVENT_ONSCROLLEDAT, new String[]{getId(), act.id, String.valueOf(index), String.valueOf(srcLeft), String.valueOf(r)});
								}
							}
						}
					} else {
						srcLeft = 0;
					}
					int dstLeft = position.rect.left + rectWidth - rest;
					rest = rest - (frame.frameWidth - srcLeft);
					int srcRight = (rest > 0) ? frame.frameWidth : frame.frameWidth + rest;
					int dstRight = dstLeft + (srcRight - srcLeft);
					frame.load();
					if (!usedFrame.contains(frame)) usedFrame.add(frame);
					canvas.drawBitmap(frame.bitmap,
								new Rect(srcLeft, 0, srcRight, frame.frameHeight),
								new Rect(dstLeft, position.rect.top, dstRight, position.rect.bottom),
								null);
					if (play && !goNext) {
						if (nextFrameOffset >= frame.frameWidth) {
							nextFrameOffset = nextFrameOffset - frame.frameWidth;
						} else {
							currentFrameIndex = index;
							currentFrameOffset = nextFrameOffset;
							goNext = true;
						}
					}
				} catch (Exception e) {
					String error = "Scrolling Cartoon running error:" + e.getLocalizedMessage();
					parser.logListener.run(parser.context, parser.browser, LogListeners.LOG_TYPE_EXCEPTION, error);
				}
			}
			index++;
			if (index > act.list.size() - 1) index = 0;
		}
		act.destroyPartially(usedFrame);

	}
	private void goRight(Canvas canvas) {

		Action act = getCurrentAction();
		if (act == null) return;

		while (currentFrameOffset < 0) {
			currentFrameIndex--;
			if (currentFrameIndex < 0) currentFrameIndex = act.list.size() - 1;
			Frame preFrame = act.list.get(currentFrameIndex);
			if (preFrame != null) currentFrameOffset = currentFrameOffset + preFrame.frameWidth;
		}

		int rectWidth = position.rect.width();

		boolean isFirst = true;

		int rest = rectWidth;
		int index = currentFrameIndex;

		ArrayList<Frame> usedFrame = new ArrayList<Frame>();
		while (rest > 0) {
			Frame frame = act.list.get(index);
			if (frame != null) {
				try {
					int srcLeft;
					if (isFirst) {
						srcLeft = (int) currentFrameOffset;
						isFirst = false;
						if (frame.reportScroll != null) {
							for (int i = 0; i < frame.reportScroll.length; i++) {
								int r = (int) frame.reportScroll[i];
								if (Math.abs(r - srcLeft) < Math.floor(step / 2f)) {
									parser.browser.eventList.run(Cartoons.EVENT_ONSCROLLEDAT, new String[]{getId(), act.id, String.valueOf(index), String.valueOf(srcLeft), String.valueOf(r)});
								}
							}
						}
					} else {
						srcLeft = 0;
					}
					int dstLeft = position.rect.left + rectWidth - rest;
					rest = rest - (frame.frameWidth - srcLeft);
					int srcRight = (rest > 0) ? frame.frameWidth : frame.frameWidth + rest;
					int dstRight = dstLeft + (srcRight - srcLeft);
					frame.load();
					if (!usedFrame.contains(frame)) usedFrame.add(frame);
					canvas.drawBitmap(frame.bitmap,
								new Rect(srcLeft, 0, srcRight, frame.frameHeight),
								new Rect(dstLeft, position.rect.top, dstRight, position.rect.bottom),
								null);
				} catch (Exception e) {
					String error = "Scrolling Cartoon running error:" + e.getLocalizedMessage();
					parser.logListener.run(parser.context, parser.browser, LogListeners.LOG_TYPE_EXCEPTION, error);
				}
			}
			index++;
			if (index > act.list.size() - 1) index = 0;
		}

		if (play) currentFrameOffset -= step;
		act.destroyPartially(usedFrame);

	}
	private void goUp(Canvas canvas) {

		Action act = getCurrentAction();
		if (act == null) return;

		int rectHeight = position.rect.height();

		int nextFrameOffset = (int) (currentFrameOffset + step);
		boolean goNext = false;
		boolean isFirst = true;

		int rest = rectHeight;
		int index = currentFrameIndex;

		ArrayList<Frame> usedFrame = new ArrayList<Frame>();
		while (rest > 0) {
			Frame frame = act.list.get(index);
			if (frame != null) {
				try {
					int srcTop;
					if (isFirst) {
						srcTop = (int) currentFrameOffset;
						isFirst = false;
						if (frame.reportScroll != null) {
							for (int i = 0; i < frame.reportScroll.length; i++) {
								int r = (int) frame.reportScroll[i];
								if (Math.abs(r - srcTop) < Math.floor(step / 2f)) {
									parser.browser.eventList.run(Cartoons.EVENT_ONSCROLLEDAT, new String[]{getId(), act.id, String.valueOf(index), String.valueOf(srcTop), String.valueOf(r)});
								}
							}
						}
					} else {
						srcTop = 0;
					}
					int dstTop = position.rect.top + rectHeight - rest;
					rest = rest - (frame.frameHeight - srcTop);
					int srcBottom = (rest > 0) ? frame.frameHeight : frame.frameHeight + rest;
					int dstBottom = dstTop + (srcBottom - srcTop);
					frame.load();
					if (!usedFrame.contains(frame)) usedFrame.add(frame);
					canvas.drawBitmap(frame.bitmap,
								new Rect(0, srcTop, frame.frameWidth, srcBottom),
								new Rect(position.rect.left, dstTop, position.rect.right, dstBottom),
								null);
					if (play && !goNext) {
						if (nextFrameOffset >= frame.frameHeight) {
							nextFrameOffset = nextFrameOffset - frame.frameHeight;
						} else {
							currentFrameIndex = index;
							currentFrameOffset = nextFrameOffset;
							goNext = true;
						}
					}
				} catch (Exception e) {
					String error = "Scrolling Cartoon running error:" + e.getLocalizedMessage();
					parser.logListener.run(parser.context, parser.browser, LogListeners.LOG_TYPE_EXCEPTION, error);
				}
			}
			index++;
			if (index > act.list.size() - 1) index = 0;
		}
		act.destroyPartially(usedFrame);
	}
	private void goDown(Canvas canvas) {

		Action act = getCurrentAction();
		if (act == null) return;

		while (currentFrameOffset < 0) {
			currentFrameIndex--;
			if (currentFrameIndex < 0) currentFrameIndex = act.list.size() - 1;
			Frame preFrame = act.list.get(currentFrameIndex);
			if (preFrame != null) currentFrameOffset = currentFrameOffset + preFrame.frameHeight;
		}

		int rectHeight = position.rect.height();

		boolean isFirst = true;

		int rest = rectHeight;
		int index = currentFrameIndex;

		ArrayList<Frame> usedFrame = new ArrayList<Frame>();
		while (rest > 0) {
			Frame frame = act.list.get(index);
			if (frame != null) {
				try {
					int srcTop;
					if (isFirst) {
						srcTop = (int) currentFrameOffset;
						isFirst = false;
						if (frame.reportScroll != null) {
							for (int i = 0; i < frame.reportScroll.length; i++) {
								int r = (int) frame.reportScroll[i];
								if (Math.abs(r - srcTop) < Math.floor(step / 2f)) {
									parser.browser.eventList.run(Cartoons.EVENT_ONSCROLLEDAT, new String[]{getId(), act.id, String.valueOf(index), String.valueOf(srcTop), String.valueOf(r)});
								}
							}
						}
					} else {
						srcTop = 0;
					}
					int dstTop = position.rect.top + rectHeight - rest;
					rest = rest - (frame.frameHeight - srcTop);
					int srcBottom = (rest > 0) ? frame.frameHeight : frame.frameHeight + rest;
					int dstBottom = dstTop + (srcBottom - srcTop);
					frame.load();
					if (!usedFrame.contains(frame)) usedFrame.add(frame);
					canvas.drawBitmap(frame.bitmap,
								new Rect(0, srcTop, frame.frameWidth, srcBottom),
								new Rect(position.rect.left, dstTop, position.rect.right, dstBottom),
								null);
				} catch (Exception e) {
					String error = "Scrolling Cartoon running error:" + e.getLocalizedMessage();
					parser.logListener.run(parser.context, parser.browser, LogListeners.LOG_TYPE_EXCEPTION, error);
				}
			}
			index++;
			if (index > act.list.size() - 1) index = 0;
		}

		if (play) currentFrameOffset -= step;
		act.destroyPartially(usedFrame);

	}

	@Override
	public void draw(Canvas canvas) {

		if (!visible) return;

		if (direction == DIRECTION_UP) {
			goUp(canvas);
		} else if (direction == DIRECTION_DOWN) {
			goDown(canvas);
		} else if (direction == DIRECTION_FORWARD) {
			goForward(canvas);
		} else if (direction == DIRECTION_BACKWARD) {
			goBackward(canvas);
		} else if (direction == DIRECTION_RIGHT) {
			goRight(canvas);
		} else {
			goLeft(canvas);
		}

	}

}
